#include "mysql_connection.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "mysql_resultset.h"

#include "mysql_statement.h"
#include "statement.h"
#include "mysql_prepared_statement.h"

#define ERR_UNKNOWN -1
#define ERR_INVALID_PARAME -2

namespace sql
{


	size_t Connection::db_count = 0;

	Connection::Connection() : mMysql(NULL)
	{
		// before firest connection
		if (db_count == 0)
		{
			mysql_library_init(-1, NULL, NULL);
		}

		db_count++;
	}

	Connection::~Connection()
	{
		Close();
		db_count--;

		// Free Mysql library for last ~connection
		if (db_count == 0)
		{
			// 
			mysql_library_end();
		}
	}

	void Connection::Close()
	{
		if (mMysql != NULL)
		{
			mysql_close(mMysql);
			mMysql = NULL;
		}
	}


	bool Connection::Init(const char* connString)
	{
		std::string connStr(connString);

		common::SplitString< std::string >(connString, ";", mConnectTocke);

		return Connectioning();
	}

	bool Connection::Init(const std::string& host, const std::string& port, const std::string& user, const std::string& pwd, const std::string& db)
	{
		mConnectTocke.push_back(host);
		mConnectTocke.push_back(port);
		mConnectTocke.push_back(user);
		mConnectTocke.push_back(pwd);
		mConnectTocke.push_back(db);

		return Connectioning();
	}

	bool Connection::Connectioning()
	{
		if (mConnectTocke.size() != 5) return false;

		const char* host = GetConnectHost();
		unsigned int  port = GetConnectPort();
		const char* user = GetConnectUser();
		const char* pwd = GetConnectPwd();
		const char* db = GetConnectDB();

		MYSQL* mysqlInit = mysql_init(NULL);
		if (mysqlInit == NULL)
		{
			//LOG_ERROR(DB_LOG, "Could not initialize Mysql connection\n");
			return false;
		}

		my_bool is_reconnection = 1;
		mysql_options(mysqlInit, MYSQL_OPT_RECONNECT, &is_reconnection);

		mMysql = mysql_real_connect(mysqlInit, host, user, pwd, db, port, NULL, CLIENT_MULTI_RESULTS);

		if (mMysql == NULL)
		{
			//LOG_ERROR(DB_LOG, "Could not connect to MySQL database at %s: %s\n", host, mysql_error(mysqlInit));
			mysql_close(mysqlInit);
			return false;
		}

		//LOG_INFO( DB_LOG, "Connected to MySQL database at %s", host );
		//LOG_INFO( DB_LOG, "MySQL client library: %s", mysql_get_client_info( ) );
		//LOG_INFO( DB_LOG, "MySQL server ver: %s ", mysql_get_server_info( mMysql ) );

		/*----------SET AUTOCOMMIT ON---------*/
		// It seems mysql 5.0.x have enabled this feature
		// by default. In crash case you can lose data!!!
		// So better to turn this off
		// ---
		// This is wrong since mangos use transactions,
		// autocommit is turned of during it.
		// Setting it to on makes atomic updates work
		if (mysql_autocommit(mMysql, 1) == 0)
			//LOG_INFO( DB_LOG, "AUTOCOMMIT SUCCESSFULLY SET TO 1" );
			;
		else
			//LOG_INFO( DB_LOG, "AUTOCOMMIT NOT SET TO 1" );
			;

			ExecuteUpdate("SET NAMES `utf8`");
		//	Execute("SET CHARACTER SET `utf8`")	
		mysql_set_character_set(mMysql, "UTF8");

		return true;
	}

	int Connection::ExecuteUpdate(const char* sql)
	{
		bool exec = do_query(sql);

		if (exec == false) return ERR_UNKNOWN;

		MYSQL_RES* result = mysql_store_result(mMysql);

		if (result != NULL)
		{
			mysql_free_result(result);
			//		return ERR_UNKNOWN;
		}

		unsigned int count = mysql_affected_rows(mMysql);
		return count;
	}

	bool Connection::ExecuteUpdateNoBack(const char* sql)
	{
		return do_query(sql);
	}

	int Connection::ExecuteInsert(const char* sql)
	{
		bool exec = do_query(sql);

		unsigned int guid = 0;

		if (exec == false) return ERR_INVALID_PARAME;

		MYSQL_RES* result = NULL;
		if ((result = mysql_store_result(mMysql)) == 0 &&
			mysql_field_count(mMysql) == 0 &&
			mysql_insert_id(mMysql) != 0)
		{
			guid = mysql_insert_id(mMysql);
		}


		if (result != NULL && mysql_error(mMysql) == 0)
		{
			mysql_free_result(result);
		}

		return guid;
	}


	bool Connection::do_query(const char* sql)
	{
		if (mMysql == NULL)
		{
			//LOG_NOTICE( DB_LOG, "try to reconnection to mysql" );
			bool is_connecting = Connectioning();
			if (is_connecting == false)
			{
				//LOG_ERROR( DB_LOG, "SQL: %s", sql );
				//LOG_ERROR( DB_LOG, "query ERROR %d : %s", mysql_errno( mMysql ), mysql_error( mMysql ) );
				return false;
			}
			else
			{
				//LOG_NOTICE(DB_LOG, "success to reconnection to mysql");
			}
		}

		int ret = mysql_query(mMysql, sql);
		if (ret != 0)
		{
			//LOG_ERROR( DB_LOG, "SQL: %s", sql );
			//LOG_ERROR( DB_LOG, "query ERROR %d : %s", mysql_errno( mMysql ), mysql_error( mMysql ) );
			Close();
			return false;
		}

		return true;
	}

	sql::ResultSet* Connection::ExecuteQuery(const char* sql, ResultSet& result_set)
	{
		bool execute = do_query(sql);
		if (execute == false) return NULL;

		result_set.Free();
		result_set.Init(mMysql);

		return &result_set;
	}

	sql::Statement* Connection::createStatement()
	{
		return new MysqlStatement(this);
	}

	sql::PreparedStatement* Connection::prepareStatement(const char* sql)
	{
		MYSQL_STMT* stmt = mysql_stmt_init(mMysql);
		if (stmt == NULL) return NULL;

		int rtn = mysql_stmt_prepare(stmt, sql, strlen(sql));
		if (rtn != 0)
		{
			mysql_stmt_close(stmt);
			return NULL;
		}

		return new PreparedStatement(stmt, this);
	}

	unsigned long Connection::GetLastInsertID()
	{
		return mysql_insert_id(mMysql);
	}

	unsigned int Connection::GetErrorCode()
	{
		return mysql_errno(mMysql);
	}

	const char* Connection::GetErrorString()
	{
		return mysql_error(mMysql);
	}

	int Connection::BeginTransaction()
	{
		if (mMysql == NULL)
		{
			//LOG_NOTICE( DB_LOG, "try to reconnection to mysql" );
			bool is_connecting = Connectioning();
			if (is_connecting == false)
			{
				//LOG_ERROR( DB_LOG, "Connect mysql faild" );
				return ERR_UNKNOWN;
			}
			else
			{
				//LOG_NOTICE( DB_LOG, "success to reconnection to mysql" );
			}
		}

		return mysql_autocommit(mMysql, 0);
	}

	int Connection::CommitTransaction()
	{
		if (mMysql == NULL)
		{
			//LOG_NOTICE( DB_LOG, "try to reconnection to mysql" );
			bool is_connecting = Connectioning();
			if (is_connecting == false)
			{
				//LOG_ERROR( DB_LOG, "Connect mysql faild" );
				return ERR_UNKNOWN;
			}
			else
			{
				//LOG_NOTICE( DB_LOG, "success to reconnection to mysql" );
			}
		}

		mysql_commit(mMysql);
		return mysql_autocommit(mMysql, 1);
	}

	int Connection::RollbackTransaction()
	{
		if (mMysql == NULL)
		{
			//LOG_NOTICE( DB_LOG, "try to reconnection to mysql" );
			bool is_connecting = Connectioning();
			if (is_connecting == false)
			{
				//LOG_ERROR( DB_LOG, "Connect mysql faild" );
				return ERR_UNKNOWN;
			}
			else
			{
				//LOG_NOTICE( DB_LOG, "success to reconnection to mysql" );
			}
		}

		mysql_rollback(mMysql);
		return mysql_autocommit(mMysql, 1);
	}

	void Connection::ClearResult()
	{
		if (mMysql == NULL) return;

		MYSQL_RES* res = NULL;
		while (mysql_next_result(mMysql) == 0)
		{
			mysql_free_result(res);
		}
	}

}

